package com.wan.data.mysql.orm.repository;

import cn.hutool.core.convert.ConverterRegistry;
import cn.hutool.core.lang.func.VoidFunc1;
import cn.hutool.db.Db;
import cn.hutool.db.handler.RsHandler;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.wan.data.mysql.DataSourceManager;
import com.wan.data.mysql.exception.DBInitializeException;
import com.wan.data.mysql.exception.DbDataException;
import com.wan.data.mysql.orm.convert.Converter;
import com.wan.data.mysql.orm.entity.AbstractEntity;
import com.wan.data.mysql.orm.entity.Entity;
import com.wan.data.mysql.orm.entity.EntityWrapper;
import com.wan.data.mysql.orm.meta.ColumnMeta;
import com.wan.data.mysql.orm.meta.MetaUtil;
import com.wan.data.mysql.orm.meta.TableMeta;
import com.wan.data.mysql.sql.EntitySql;
import com.wan.util.ObjectUtil;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 最下层数据仓库，数据直接落地
 */
@SuppressWarnings("unchecked")
public class EntityRepository<Pk extends Serializable,E extends Entity<Pk>> implements Repository<Pk,E> {

    private static final Log log = LogFactory.get();
    protected final Db db;
    private final EntitySql entitySql;
    protected final TableMeta tableMeta;

    protected EntityRepository() {
        Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
        Class<E> entityClass_ = (Class<E>) types[1];
        try {
            tableMeta = MetaUtil.getTableMeta(entityClass_);
            //创建表
            DataSourceManager.createTableIfNotExist(tableMeta);
            //修复同步表结构
            DataSourceManager.fixTable(tableMeta);
            db = DataSourceManager.getDbThrow(tableMeta.getDatabaseName());
            entitySql = new EntitySql(tableMeta);
        }catch (Throwable e){
            throw new DBInitializeException("数据仓库" + entityClass_.getSimpleName() + "初始化失败",e);
        }
        REPOSITORY_MAP.put(entityClass_,this);
    }


    @Override
    public E get(Pk pk) {
        try {
            return db.query(entitySql.selectById(1),
                    (RsHandler<E>) resultSet ->{
                        if(resultSet.next()){
                            return EntityWrapper.wrap(tableMeta,resultSet);
                        }
                        return null;
                    }, pk);
        } catch (SQLException throwable) {
            log.error(throwable,"DB获取数据异常entity:{},id:{}",tableMeta.getEntityType().getCanonicalName(),pk);
        }
        return null;
    }

    @Override
    public Collection<E> get(Collection<Pk> pks) {
        try {
            return db.query(entitySql.selectById(pks.size()),
                    (RsHandler<List<E>>) resultSet ->{
                        List<E> list  = new ArrayList<>();
                        while (resultSet.next()){
                            list.add(EntityWrapper.wrap(tableMeta,resultSet));
                        }
                        return list;
                    }, pks.toArray());
        } catch (SQLException throwable) {
            log.error(throwable,"DB获取数据异常entity:{},id:{}",tableMeta.getEntityType().getCanonicalName(),pks);
        }
        return Collections.emptyList();
    }

    @Override
    public void delete(Collection<Pk> list) {
        try {
            db.execute(entitySql.delete(list.size()),list.toArray());
        }catch (Exception e){
            log.error(e,"DB删除数据异常entity:{},id:{}",tableMeta.getEntityType().getCanonicalName(),list);
        }
    }

    @Override
    public Map<Pk, E> getAll() {
        return getByCondition("");
    }

    /**
     * 根据条件查询
     */
    public Map<Pk,E> getByCondition(String condition,Object...args){
        try {
            return db.query(entitySql.selectByCondition(condition), (RsHandler<Map<Pk, E>>) resultSet -> {
                Map<Pk,E> map = new HashMap<>();
                while (resultSet.next()){
                    E e = EntityWrapper.wrap(tableMeta,resultSet);
                    if(e != null){
                        map.put(e.getPk(),e);
                    }
                }
                return map;
            },args);
        } catch (SQLException throwable) {
            log.error(throwable,"根据条件获取数据异常 entity:{},condition:{}",tableMeta.getEntityType().getCanonicalName(),condition);
            return Collections.EMPTY_MAP;
        }
    }

    @Override
    public void insert(E e) {
        try {
            String insertOne = entitySql.insertOne();
            checkEntity(e);
            ColumnMeta autoColumn = tableMeta.getAutoColumn();
            if(autoColumn != null){
                Long autoId = db.executeForGeneratedKey(insertOne, EntityWrapper.unWrap(e));
                //Number类型转换,防止类型长度溢出
                Object convert = ConverterRegistry.getInstance().convert(autoColumn.getFieldType(), autoId);
                //经过Orm的转换器,处理例如AtomicLong等类型
                Converter converter = Converter.get(autoColumn);
                autoColumn.getFastField().set(e,converter.dbToEntity(autoColumn,convert));
            }else{
                db.execute(insertOne,EntityWrapper.unWrap(e));
            }
        }catch (Exception exception){
            log.error(exception,"DB插入数据异常entity:{},id:{}",tableMeta.getEntityType().getCanonicalName(),e);
        }
    }

    /**
     * 批量操作
     */
    private void batch(String sql,List<Object[]> params){
        VoidFunc1<Db> dbVoidFunc1 = db ->{
            int[] ints = db.executeBatch(sql, params);
            long count = Arrays.stream(ints).filter(i -> i > 0 || i == Statement.SUCCESS_NO_INFO).count();
            if(count < params.size()){
                log.error("批量处理数据成功数:{},失败:{},sql:{}",count,params.size() - count,sql);
            }
        };
        //长度大于10，使用事务提交，性能最大能提升10倍
        try {
            if(params.size() > 10){
                db.tx(dbVoidFunc1);
            }else{
                dbVoidFunc1.call(db);
            }
        }catch (Throwable e){
            log.error(e,"DB批量处理数据异常entity:{},sql:{}",
                    tableMeta.getEntityType().getCanonicalName(),sql);
        }
    }

    @Override
    public void update(Collection<E> list) {
        List<Object[]> params = list.stream().map(e->{
            checkEntity(e);
            Object[] objects = EntityWrapper.unWrap(e);
            Object[] copy = Arrays.copyOf(objects, objects.length + 1);
            copy[copy.length - 1] = e.getPk();
            return copy;
        }).collect(Collectors.toList());
        batch(entitySql.update(),params);
    }

    /**
     * 替换数据
     * 必须有主键，表里不存在则insert，存在则先删除原数据，再insert
     */
    @Override
    public void replaceInto(Collection<E> list) {
        for (E e : list) {
            checkEntity(e);
        }
        List<Object[]> params = list.stream().map(e->EntityWrapper.unWrap(e,true)).collect(Collectors.toList());
        batch(entitySql.replaceInto(),params);
    }

    @Override
    public void save(Collection<E> list) {
        Iterator<E> iterator = list.iterator();
        while (iterator.hasNext()){
            E next = iterator.next();
            try {
                checkEntity(next);
                if(isInsert(next)){
                    iterator.remove();
                }
            }catch (Exception exception){
                log.error(exception);
                iterator.remove();
            }
        }
        replaceInto(list);
    }

    @Override
    public void save(E e) {
        try {
            checkEntity(e);
            if(isInsert(e)) return;
            this.replaceInto(e);
        }catch (Exception exception){
            log.error(exception);
        }
    }

    /**
     * 是否直接插入，主要为了获取自增值
     */
    protected boolean isInsert(E e) throws InvocationTargetException {
        ColumnMeta autoColumn = tableMeta.getAutoColumn();
        if(autoColumn != null){
            Object obj = autoColumn.getFastField().get(e);
            if(ObjectUtil.isNullOrZero(obj)){
                insert(e);
                return true;
            }
        }
        return false;
    }

    /**
     * 校验实体
     */
    protected void checkEntity(E e){
        if(!tableMeta.getPkColumn().isAutoIncrement()){
            Pk pk = e.getPk();
            if(ObjectUtil.isEmptyOrZero(pk)){
                throw new DbDataException("非自增主键不能为空或者0， type:" + tableMeta.getEntityType().getCanonicalName());
            }
        }
        if(e instanceof AbstractEntity){
            AbstractEntity<Pk> abstractEntity = (AbstractEntity<Pk>) e;
            abstractEntity.setModifyTime(new Date());
        }
    }

}
